home *** CD-ROM | disk | FTP | other *** search
- ;;; -*- Package: ASSEMBLER; Log: C.Log -*-
- ;;;
- ;;; **********************************************************************
- ;;; This code was written as part of the CMU Common Lisp project at
- ;;; Carnegie Mellon University, and has been placed in the public domain.
- ;;; If you want to use this code or any part of CMU Common Lisp, please contact
- ;;; Scott Fahlman or slisp-group@cs.cmu.edu.
- ;;;
- (ext:file-comment
- "$Header: assem-opt.lisp,v 1.5 91/02/25 15:31:24 ram Exp $")
- ;;;
- ;;; **********************************************************************
- ;;;
- ;;; Assembly level optimization for the compiler.
- ;;;
- (defpackage "ASSEMBLER"
- (:use "LISP" "EXTENSIONS" "C")
- (:export "OPTIMIZE-SEGMENT")
- (:import-from "C" "BACKEND-SB-LIST" "FINITE-SB-LIVE-TNS" "FINITE-SB"
- "SC-ELEMENT-SIZE"))
- (in-package "ASSEMBLER")
-
- ;;; DELETE-NODE -- Internal
- ;;;
- ;;; Delete a node from the assembly output. Seg is the segment, so we can
- ;;; fix up the Last pointer.
- ;;;
- (defun delete-node (inst seg)
- (declare (type node inst) (type segment seg))
- (let ((prev (node-prev inst))
- (next (node-next inst)))
- (setf (node-next prev) next)
- (cond (next (setf (node-prev next) prev))
- (t
- (assert (eq inst (segment-last seg)))
- (setf (segment-last seg) prev)))))
- (undefined-value))
-
-
- ;;; REPLACE-NODE -- Internal
- ;;;
- ;;; Replace Node with New, deleting Node. Seg is the segment, so we can fix
- ;;; up the Last pointer.
- ;;;
- (defun replace-node (node new seg)
- (declare (type node new new) (type segment seg))
- (let ((prev (node-prev node))
- (next (node-next node)))
- (delete-node new seg)
- (setf (node-prev new) prev)
- (setf (node-next new) next)
- (setf (node-next prev) new)
- (cond (next (setf (node-prev next) new))
- (t
- (assert (eq node (segment-last seg)))
- (setf (segment-last seg) new))))
- (undefined-value))
-
-
- ;;; [NOT-]INST-CLASS-P -- Internal
- ;;;
- ;;; If Inst is an Instruction with some of the specified Attributes, then
- ;;; return T, otherwise NIL. NOT-INST-CLASS-P is like (NOT (INST-CLASS-P ...))
- ;;; except that we still return NIL when INST is not an instruction.
- ;;;
- (defmacro inst-class-p (inst &rest attributes)
- (once-only ((n-inst inst))
- `(and (typep ,n-inst 'instruction)
- (instruction-attributep (instruction-info-attributes
- (instruction-info ,n-inst))
- ,@attributes))))
- ;;;
- (defmacro not-inst-class-p (inst &rest attributes)
- (once-only ((n-inst inst))
- `(and (typep ,n-inst 'instruction)
- (not (instruction-attributep (instruction-info-attributes
- (instruction-info ,n-inst))
- ,@attributes)))))
-
-
- ;;; PREV-INST, NEXT-INST -- Internal
- ;;;
- ;;; Return the next or previous instruction of Node, if we can determine
- ;;; this. NIL if we can't tell.
- ;;;
- (declaim (inline next-inst prev-inst))
- (defun prev-inst (node)
- (declare (type (or node null) node))
- (when node
- (let ((prev (node-prev node)))
- (when (typep prev 'instruction) prev))))
- ;;;
- (defun next-inst (node)
- (declare (type (or node null) node))
- (when node
- (do ((node (node-next node) (node-next node)))
- (nil)
- (typecase node
- (instruction (return node))
- (label)
- (t (return nil))))))
-
-
- ;;; NOTE-TN-USED -- Internal
- ;;;
- ;;; Mark the locations for TN as being in use (thus prohibiting motion of
- ;;; code that also uses these locations.)
- ;;;
- (defun note-tn-used (tn clobber-p)
- (let* ((sc (tn-sc tn))
- (sb (sc-sb sc)))
- (when (typep sb 'finite-sb)
- (let ((live (finite-sb-live-tns sb)))
- (loop for i from (tn-offset tn)
- repeat (sc-element-size sc) do
- (setf (svref live i)
- (if clobber-p :clobber (or (svref live i) :use)))))))
- (undefined-value))
-
-
- ;;; TN-USED-P -- Internal
- ;;;
- ;;; Return :USE, :CLOBBER or NIL, depending on how of TN's locations are
- ;;; currently used.
- ;;;
- (defun tn-used-p (tn)
- (let* ((sc (tn-sc tn))
- (sb (sc-sb sc)))
- (when (typep sb 'finite-sb)
- (let ((live (finite-sb-live-tns sb))
- (res nil))
- (loop for i from (tn-offset tn)
- repeat (sc-element-size sc) do
- (ecase (svref live i)
- ((nil))
- (:use (unless res (setq res :use)))
- (:clobber (setq res :clobber))))
- res))))
-
-
- ;;; FIND-DELAY-SUBJECT -- Internal
- ;;;
- ;;; Find an instruction that can be moved into the delay slot of Delay and
- ;;; return it, or NIL if we can't find any. We scan backward for a preceding
- ;;; instruction that doesn't have any resource conflicts with any intervening
- ;;; instructions. There is a resource conflict if:
- ;;; -- Any used random resources are clobbered by subsequent instructions, or
- ;;; -- Any clobbered random resources are used *or* clobbered by subsequent
- ;;; instructions, or
- ;;; -- Any arguments to the instruction are results of subsequent
- ;;; instructions, or
- ;;; -- Any results of the instruction are either arguments *or* results to
- ;;; subsequent instructions.
- ;;;
- ;;; We also stop the scan whenever we hit a non-instruction (label or .align)
- ;;; or a pinned instruction. The instruction must not be:
- ;;; -- In a delay slot itself, or
- ;;; -- The delayed instruction itself, or
- ;;; -- An instruction with a delay slot itself, or
- ;;; -- A no-op itself.
- ;;;
- ;;; We put an arbitrary upper bound of 20 on how far we scan back to avoid any
- ;;; potential quadratic blowup in large blocks.
- ;;;
- (defun find-delay-subject (delay)
- (dolist (sb (backend-sb-list *backend*))
- (when (typep sb 'finite-sb)
- (fill (finite-sb-live-tns sb) nil)))
-
- (let ((used-resources 0)
- (clobbered-resources 0))
- (declare (type index used-resources))
- (loop for inst = delay then (node-prev inst)
- repeat 20
- while (typep inst 'instruction) do
- (let* ((info (instruction-info inst))
- (use (instruction-info-use info))
- (clobber (instruction-info-clobber info)))
- (unless (eq inst delay)
- (when (instruction-info-pinned info)
- (return nil))
- (when (and (zerop (logand use clobbered-resources))
- (zerop (logand clobber (logior used-resources
- clobbered-resources)))
- (do-arguments (arg inst t)
- (when (eq (tn-used-p arg) :clobber) (return nil)))
- (do-results (res inst t)
- (when (tn-used-p res) (return nil)))
- (not-inst-class-p inst nop delayed-branch delayed-load)
- (not-inst-class-p (prev-inst inst)
- delayed-branch delayed-load))
- (return inst)))
-
- (do-results (res inst)
- (note-tn-used res t))
- (do-arguments (arg inst)
- (note-tn-used arg nil))
- (setq used-resources (logior use used-resources))
- (setq clobbered-resources (logior clobber clobbered-resources))))))
-
-
- ;;; OPTIMIZE-SEGMENT -- Public
- ;;;
- ;;; Do assembly-level optimization on Seg. Currently this consists solely
- ;;; of no-op elimination.
- ;;;
- (defun optimize-segment (seg)
- (do ((current (node-next seg) (node-next current)))
- ((null current))
- (block NEXT
- (when (inst-class-p current nop)
- (let ((prev (prev-inst current)))
- (when (and (inst-class-p prev delayed-load)
- (not-inst-class-p (prev-inst prev) delayed-branch))
- (let ((next (next-inst current)))
- (when (and next
- (block punt
- (do-arguments (arg next t)
- (do-results (res prev)
- (when (location= arg res)
- (return-from punt nil))))))
- (delete-node current seg)
- (return-from NEXT))))
- (when (inst-class-p prev delayed-branch delayed-load)
- (let ((subj (find-delay-subject prev)))
- (when subj
- (replace-node current subj seg)
- (return-from NEXT)))))))))
-